#ifndef UDPSERVER_HPP
#define UDPSERVER_HPP

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <cstring>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>

#include "log.hpp"

#define BUF_SIZE 1024

using Echofunc_t = std::function<std::string(const std::string &,
                                             const std::string &, uint16_t)>;

Log log_;

enum { SOCK_CREATE_FAIL = 1, SOCK_BIND_FAIL };

class UdpServer {
 public:
  UdpServer(const uint16_t port = defaultport)
      : sockfd_(0), port_(port), isrunning_(false) {}

  ~UdpServer() {}

  // 初始化服务器
  void Init() {
    sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd_ < 0) {
      log_(FATAL, "socket create fail , the errornum : %d\n", sockfd_);
      exit(SOCK_CREATE_FAIL);
    }
    log_(INFO, "socket create sucess , sockfd : %d", sockfd_);

    struct sockaddr_in localsr;
    bzero(&localsr, sizeof(localsr));
    localsr.sin_family = AF_INET;
    localsr.sin_port = htons(port_);
    localsr.sin_addr.s_addr = INADDR_ANY;
    socklen_t locallen = sizeof(localsr);

    if (bind(sockfd_, (const struct sockaddr *)&localsr, locallen) < 0) {
      log_(FATAL, "socket bind fail, err string :%s", strerror(errno));
      exit(SOCK_BIND_FAIL);
    }
    log_(INFO, "socket bind sucess , sockfd : %d", sockfd_);
  }

  void CheckUsr(const struct sockaddr_in &client) {
    // 检查用户IP是否是第一次登录
    uint16_t port = ntohs(client.sin_port);
    std::string ip = inet_ntoa(client.sin_addr);

    auto it = online_user_.find(ip);
    if (it == online_user_.end()) {
      online_user_[ip] = client;
      std::cout << "The " << ip << " first login..." << std::endl;
    } else
      ;
  }

  void Broadcast(const std::string &info, const std::string &ip,
                 uint16_t port) {
    for (const auto &usr : online_user_) {
      std::string massage =
          "[" + ip + ":" + std::to_string(port) + " echo]# " + info;
      socklen_t len = sizeof(usr.second);
      sendto(sockfd_, massage.c_str(), massage.size(), 0,
             (struct sockaddr *)(&usr.second), len);
    }
  }

  // 运行服务器
  void Run(Echofunc_t EchoHandler) {
    isrunning_ = true;
    char inbuf[BUF_SIZE] = {0};
    while (isrunning_) {
      struct sockaddr_in client;
      socklen_t len = sizeof(client);
      bzero(&client, sizeof(client));

      size_t n = recvfrom(sockfd_, inbuf, sizeof(inbuf) - 1, 0,
                          (struct sockaddr *)&client, &len);

      if (n < 0) {
        log_(WARNING, "recvfrom fail, err string :%s", strerror(errno));
        continue;
      }

      uint16_t port = ntohs(client.sin_port);
      std::string ip = inet_ntoa(client.sin_addr);
      inbuf[n] = 0;

      CheckUsr(client);
      std::string info = inbuf;

      Broadcast(info, ip, port);
    }
  }

 private:
  int sockfd_;
  uint16_t port_;
  bool isrunning_;

  static const uint16_t defaultport;
  std::unordered_map<std::string, struct sockaddr_in> online_user_;
  // 用于管理用户列表
};

const uint16_t UdpServer::defaultport = 8080;

#endif
